Os livros de linguagem de programação explicam que os tipos de valor são criados na pilha e os tipos de referência são criados na pilha, sem explicar o que essas duas coisas são. Eu não li uma explicação clara disso. Eu entendo o que é uma pilha. Mas, Onde e o que estão (fisicamente na memória de um computador real)? Até que ponto eles são controlados pelo sistema operacional ou pelo tempo de execução da linguagem? Qual é o seu escopo? O que determina o tamanho de cada um deles? O que o torna mais rápido?
2020-12-07 21:42:22
A pilha é a memória reservada como espaço temporário para um thread de execução. Quando uma função é chamada, um bloco é reservado no topo da pilha para variáveis locais e alguns dados de contabilidade. Quando essa função retorna, o bloco deixa de ser usado e pode ser usado na próxima vez que uma função for chamada. A pilha é sempre reservada em uma ordem LIFO (último a entrar, primeiro a sair); o bloco reservado mais recentemente é sempre o próximo bloco a ser liberado. Isso torna muito simples controlar a pilha; liberar um bloco da pilha nada mais é do que ajustar um ponteiro. A pilha é a memória reservada para alocação dinâmica. Ao contrário da pilha, não há um padrão imposto para a alocação e desalocação de blocos do heap; você pode alocar um bloco a qualquer momento e liberá-lo a qualquer momento. Isso torna muito mais complexo controlar quais partes do heap estão alocadas ou livres a qualquer momento; há muitos alocadores de heap customizados disponíveis para ajustar o desempenho de heap para diferentes padrões de uso. Cada thread obtém uma pilha, embora normalmente haja apenas um heap para o aplicativo (embora não seja incomum ter vários heaps para diferentes tipos de alocação). Para responder às suas perguntas diretamente: Até que ponto eles são controlados pelo sistema operacional ou pelo tempo de execução da linguagem? O SO aloca a pilha para cada encadeamento no nível do sistema quando o encadeamento é criado. Normalmente, o sistema operacional é chamado pelo tempo de execução da linguagem para alocar o heap para o aplicativo. Qual é o seu escopo? A pilha é anexada a um encadeamento, portanto, quando o encadeamento sai, a pilha é recuperada. O heap é normalmente alocado na inicialização do aplicativo pelo tempo de execução e é recuperado quando o aplicativo (tecnicamente processo) sai. O que determina o tamanho de cada um deles? O tamanho da pilha é definido quando um thread é criado. O tamanho do heap é definido na inicialização do aplicativo, mas pode aumentar conforme o espaço é necessário (o alocador solicita mais memória do sistema operacional). O que o torna mais rápido? A pilha é mais rápida porque o padrão de acesso torna trivial alocar e desalocar memória dela (um ponteiro / inteiro é simplesmente incrementado ou decrementado), enquanto o heap tem uma contabilidade muito mais complexa envolvida em uma alocação ou desalocação. Além disso, cada byte na pilha tende a ser reutilizado com muita frequência, o que significa que tende a ser mapeado para o cache do processador, tornando-o muito rápido. Outro impacto de desempenho para o heap é que o heap, sendo principalmente um recurso global, normalmente tem que ser multi-threading seguro, ou seja, cada alocação e desalocação precisa ser - normalmente - sincronizada com "todos" os outros acessos do heap no programa. Uma demonstração clara: Fonte da imagem: vikashazrati.wordpress.com | Pilha: Armazenado na RAM do computador exatamente como o heap. As variáveis criadas na pilha sairão do escopo e serão desalocadas automaticamente. Muito mais rápido para alocar em comparação com as variáveis na pilha. Implementado com uma estrutura de dados de pilha real. Armazena dados locais, endereços de retorno, usados para passagem de parâmetros. Pode haver um estouro de pilha quando muito da pilha é usado (principalmente de recursão infinita ou muito profunda, alocações muito grandes). Os dados criados na pilha podem ser usados sem ponteiros. Você usaria a pilha se soubesse exatamente quantos dados precisa alocar antes do tempo de compilação e não fosse muito grande. Normalmente tem um tamanho máximo já determinado quando o programa inicia. Pilha: Armazenado na RAM do computador exatamente como a pilha. Em C ++, as variáveis no heap devem ser destruídas manualmente e nunca sair do escopo. Os dados são liberados com delete, delete [] ou free. Mais lento para alocar em comparação com as variáveis na pilha. Usado sob demanda para alocar um bloco de dados para uso pelo programa. Pode haver fragmentação quando há muitas alocações e desalocações. Em C ++ ou C, os dados criados no heap serão apontados por ponteiros e alocados com new ou malloc respectivamente. Pode haver falhas de alocação se um buffer muito grande for solicitado para ser alocado. Você usaria o heap se não souber exatamente de quantos dados precisará em tempo de execução ou se precisar alocar muitos dados. Responsável por vazamentos de memória. Exemplo: int foo () { char * pBuffer; // <- nada alocado ainda (excluindo o próprio ponteiro, que é alocado aqui na pilha). bool b = verdadeiro; // Alocado na pilha. if (b) { // Cria 500 bytes na pilha buffer char [500]; // Cria 500 bytes na pilha pBuffer = novo char [500]; } // <- buffer é desalocado aqui, pBuffer não é } // <--- opa, há um vazamento de memória, eu deveria ter chamado delete [] pBuffer; | O ponto mais importante é que heap e pilha são termos genéricos para as maneiras como a memória pode ser alocada. Eles podem ser implementados de muitas maneiras diferentes e os termos se aplicam aos conceitos básicos. Em uma pilha de itens, os itens ficam um em cima do outro na ordem em que foram colocados, e você só pode remover o de cima(sem derrubar a coisa toda). A simplicidade de uma pilha é que você não precisa manter uma tabela contendo um registro de cada seção de memória alocada; a única informação de estado necessária é um único ponteiro para o final da pilha. Para alocar e desalocar, basta incrementar e decrementar esse único ponteiro. Nota: às vezes, uma pilha pode ser implementada para começar no topo de uma seção da memória e se estender para baixo em vez de crescer para cima. Em uma pilha, não há uma ordem específica para a maneira como os itens são colocados. Você pode acessar e remover itens em qualquer ordem porque não há um item 'superior' claro. A alocação de heap requer a manutenção de um registro completo da memória alocada e do que não é, bem como alguma manutenção de sobrecarga para reduzir a fragmentação, localizar segmentos de memória contíguos grandes o suficiente para caber no tamanho solicitado e assim por diante. A memória pode ser desalocada a qualquer momento, deixando espaço livre. Às vezes, um alocador de memória realizará tarefas de manutenção, como desfragmentar a memória movendo a memória alocada ou coletando lixo - identificando em tempo de execução quando a memória não está mais no escopo e desalocando-a. Essas imagens devem fazer um trabalho razoavelmente bom ao descrever as duas maneiras de alocar e liberar memória em uma pilha e um heap. Yum! Até que ponto eles são controlados pelo sistema operacional ou pelo tempo de execução da linguagem? Conforme mencionado, heap e stack são termos gerais e podem ser implementados de várias maneiras. Os programas de computador normalmente têm uma pilha chamada pilha de chamadas que armazena informações relevantes para a função atual, como um ponteiro para qualquer função a partir da qual foi chamada e quaisquer variáveis locais. Como as funções chamam outras funções e depois retornam, a pilha aumenta e diminui para conter informações das funções mais abaixo na pilha de chamadas. Um programa realmente não tem controle de tempo de execução sobre ele; é determinado pela linguagem de programação, sistema operacional e até mesmo pela arquitetura do sistema. Um heap é um termo geral usado para qualquer memória alocada dinâmica e aleatoriamente; ou seja, fora de serviço. A memória é normalmente alocada pelo sistema operacional, com o aplicativo chamando as funções da API para fazer essa alocação. Há um pouco de sobrecarga necessária no gerenciamento de memória alocada dinamicamente, que geralmente é tratada pelo código de tempo de execução da linguagem de programação ou ambiente usado. Qual é o seu escopo? A pilha de chamadas é um conceito de nível tão baixo que não se relaciona ao 'escopo' no sentido de programação. Se você desmontar algum código, verá referências de estilo de ponteiro relativo para partes da pilha, mas no que diz respeito a uma linguagem de nível superior, a linguagem impõe suas próprias regras de escopo. Um aspecto importante de uma pilha, entretanto, é que uma vez que uma função retorna, qualquer coisa local para aquela função é imediatamente liberada da pilha. Isso funciona da maneira que você espera, considerando como suas linguagens de programação funcionam. Em uma pilha, também é difícil de definir. O escopo é tudo o que é exposto pelo sistema operacional, mas sua linguagem de programação provavelmente adiciona suas regras sobre o que é um "escopo" em seu aplicativo. A arquitetura do processador e o sistema operacional usam endereçamento virtual, que o processador traduz em endereços físicos e há falhas de página, etc. Eles controlam quais páginas pertencem a quais aplicativos. Você nunca realmente precisa se preocupar com isso, porque você apenas usa qualquer método que sua linguagem de programação usa para alocar e liberar memória e verificar se há erros (se a alocação / liberação falhar por algum motivo). O que determina o tamanho de cada um deles? Novamente, isso depende da linguagem, compilador, sistema operacional e arquitetura. Uma pilha geralmente é pré-alocada porque, por definição, deve ser uma memória contígua. O compilador da linguagem ou o sistema operacional determina seu tamanho. Você não armazena grandes blocos de dados na pilha, portanto, será grande o suficiente para nunca ser totalmente usado, exceto em casos de recursão infinita indesejada (portanto, "estouro de pilha") ou outras decisões de programação incomuns. Um heap é um termo geral para qualquer coisa que pode ser alocada dinamicamente. Dependendo de como você olha para ele, ele muda constantemente de tamanho. Em processadores e sistemas operacionais modernos, a maneira exata como funciona é muito abstrata de qualquer maneira, então você normalmente não precisa se preocupar muito com como funciona no fundo, exceto que (em linguagens onde isso permite) você não deve usar memória que você ainda não alocou ou memória que você liberou. O que o torna mais rápido? A pilha é mais rápida porque toda a memória livre é sempre contígua. Nenhuma lista precisa ser mantida de todos os segmentos de memória livre, apenas um único ponteiro para o topo atual da pilha. Os compiladores geralmente armazenam esse ponteiro em um registro especial e rápido para esse propósito. Além do mais, as operações subsequentes em uma pilha são geralmente concentradas em áreas muito próximas da memória, o que em um nível muito baixo é bom para otimização pelo processador no chipcaches. | (Eu movi esta resposta de outra pergunta que era mais ou menos uma idiotice desta.) A resposta à sua pergunta é específica da implementação e pode variar entre compiladores e arquiteturas de processador. No entanto, aqui está uma explicação simplificada. A pilha e o heap são áreas de memória alocadas do sistema operacional subjacente (geralmente memória virtual mapeada para memória física sob demanda). Em um ambiente multithread, cada thread terá sua própria pilha completamente independente, mas eles compartilharão o heap. O acesso simultâneo deve ser controlado no heap e não é possível na pilha. A pilha O heap contém uma lista vinculada de blocos usados e livres. Novas alocações no heap (por new ou malloc) são satisfeitas criando um bloco adequado de um dos blocos livres. Isso requer a atualização da lista de blocos no heap. Essas metainformações sobre os blocos no heap também são armazenadas no heap, geralmente em uma pequena área na frente de cada bloco. À medida que o heap cresce, novos blocos são freqüentemente alocados de endereços inferiores para endereços superiores. Portanto, você pode pensar no heap como um heap de blocos de memória que aumenta de tamanho conforme a memória é alocada. Se o heap for muito pequeno para uma alocação, o tamanho pode ser aumentado com a aquisição de mais memória do sistema operacional subjacente. Alocar e desalocar muitos blocos pequenos pode deixar o heap em um estado onde há muitos pequenos blocos livres intercalados entre os blocos usados. Uma solicitação para alocar um bloco grande pode falhar porque nenhum dos blocos livres é grande o suficiente para satisfazer a solicitação de alocação, embora o tamanho combinado dos blocos livres possa ser grande o suficiente. Isso é chamado de fragmentação de heap. Quando um bloco usado adjacente a um bloco livre é desalocado, o novo bloco livre pode ser mesclado com o bloco livre adjacente para criar um bloco livre maior, reduzindo efetivamente a fragmentação do heap. A pilha A pilha geralmente funciona em conjunto com um registro especial na CPU denominado ponteiro da pilha. Inicialmente, o ponteiro da pilha aponta para o topo da pilha (o endereço mais alto na pilha). A CPU tem instruções especiais para colocar valores na pilha e retirá-los da pilha. Cada push armazena o valor na localização atual do ponteiro da pilha e diminui o ponteiro da pilha. Um pop recupera o valor apontado pelo ponteiro da pilha e então aumenta o ponteiro da pilha (não se confunda pelo fato de que adicionar um valor à pilha diminui o ponteiro da pilha e remover um valor o aumenta. Lembre-se de que a pilha cresce para o fundo). Os valores armazenados e recuperados são os valores dos registros da CPU. Quando uma função é chamada, a CPU usa instruções especiais que empurram o ponteiro da instrução atual, ou seja, o endereço do código em execução na pilha. A CPU então salta para a função definindo o ponteiro de instrução para o endereço da função chamada. Posteriormente, quando a função retorna, o ponteiro de instrução antigo é retirado da pilha e a execução é retomada no código logo após a chamada da função. Quando uma função é inserida, o ponteiro da pilha é diminuído para alocar mais espaço na pilha para variáveis locais (automáticas). Se a função tiver uma variável local de 32 bits, quatro bytes serão colocados de lado na pilha. Quando a função retorna, o ponteiro da pilha é movido de volta para liberar a área alocada. Se uma função tiver parâmetros, eles serão colocados na pilha antes da chamada da função. O código na função é então capaz de navegar para cima na pilha a partir do ponteiro da pilha atual para localizar esses valores. As chamadas de função de aninhamento funcionam perfeitamente. Cada nova chamada alocará parâmetros de função, o endereço de retorno e espaço para variáveis locais e esses registros de ativação podem ser empilhados para chamadas aninhadas e serão desenrolados da maneira correta quando as funções retornarem. Como a pilha é um bloco limitado de memória, você pode causar um estouro de pilha chamando muitas funções aninhadas e / ou alocando muito espaço para variáveis locais. Freqüentemente, a área de memória usada para a pilha é configurada de forma que escrever abaixo da parte inferior (o endereço mais baixo) da pilha acionará uma armadilha ou exceção na CPU. Essa condição excepcional pode então ser capturada pelo tempo de execução e convertida em algum tipo de exceção de estouro de pilha. Uma função pode ser alocada no heap em vez de em uma pilha? Não, os registros de ativação para funções (ou seja, variáveis locais ou automáticas) são alocados na pilha que é usada não apenas para armazenar essas variáveis, mas também para controlar as chamadas de função aninhadas. O modo como o heap é gerenciado depende realmente do ambiente de tempo de execução. C usa malloc e C ++ usa new, mas muitas outras linguagens têm coleta de lixo. No entanto, a pilha é um recurso de nível mais baixo intimamente ligado à arquitetura do processador. Aumentar a pilha quando não há espaço suficiente não é muito difícil, poisele pode ser implementado na chamada da biblioteca que lida com o heap. No entanto, aumentar a pilha geralmente é impossível, pois o estouro da pilha só é descoberto quando é tarde demais; e encerrar o thread de execução é a única opção viável. | No seguinte código C # public void Method1 () { int i = 4; int y = 2; classe1 cls1 = nova classe1 (); } Veja como a memória é gerenciada Variáveis locais que só precisam durar enquanto a invocação da função entrar na pilha. O heap é usado para variáveis cujo tempo de vida realmente não sabemos de antemão, mas esperamos que durem algum tempo. Na maioria das linguagens, é essencial saber em tempo de compilação o tamanho de uma variável se quisermos armazená-la na pilha. Os objetos (que variam em tamanho à medida que os atualizamos) vão para o heap porque não sabemos no momento da criação quanto tempo eles irão durar. Em muitas linguagens, o heap é coletado como lixo para localizar objetos (como o objeto cls1) que não têm mais referências. Em Java, a maioria dos objetos vai diretamente para o heap. Em linguagens como C / C ++, estruturas e classes podem frequentemente permanecer na pilha quando você não está lidando com ponteiros. Mais informações podem ser encontradas aqui: A diferença entre pilha e alocação de memória heap «timmurphy.org e aqui: Criação de objetos na pilha e no heap Este artigo é a fonte da imagem acima: Seis conceitos importantes do .NET: Stack, heap, tipos de valor, tipos de referência, boxing e unboxing - CodeProject mas esteja ciente de que pode conter algumas imprecisões. | A pilha Quando você chama uma função, os argumentos dessa função mais alguma outra sobrecarga são colocados na pilha. Algumas informações (como para onde ir no retorno) também são armazenadas lá. Quando você declara uma variável dentro de sua função, essa variável também é alocada na pilha. Desalocar a pilha é muito simples porque você sempre desaloca na ordem inversa em que alocou. Stack stuff é adicionado conforme você entra nas funções, os dados correspondentes são removidos quando você sai delas. Isso significa que você tende a permanecer em uma pequena região da pilha, a menos que chame muitas funções que chamam muitas outras funções (ou crie uma solução recursiva). A pilha O heap é um nome genérico para o local onde você coloca os dados que cria na hora. Se você não sabe quantas espaçonaves seu programa vai criar, é provável que use o novo operador (ou malloc ou equivalente) para criar cada nave. Essa alocação vai durar um pouco, então é provável que liberemos as coisas em uma ordem diferente daquela em que as criamos. Assim, o heap é muito mais complexo, porque acabam havendo regiões da memória que não são utilizadas intercaladas com pedaços que são - a memória fica fragmentada. Encontrar memória livre do tamanho necessário é um problema difícil. É por isso que o heap deve ser evitado (embora ainda seja usado com frequência). Implementação A implementação da pilha e do heap geralmente é feita no tempo de execução / sistema operacional. Freqüentemente, jogos e outros aplicativos de desempenho crítico criam suas próprias soluções de memória que pegam uma grande parte da memória do heap e a distribuem internamente para evitar depender do sistema operacional para obter memória. Isso só é prático se o uso de sua memória for bem diferente do normal - ou seja, para jogos em que você carrega um nível em uma grande operação e pode jogar tudo fora em outra grande operação. Localização física na memória Isso é menos relevante do que você pensa por causa de uma tecnologia chamada Memória Virtual, que faz seu programa pensar que você tem acesso a um certo endereço onde os dados físicos estão em outro lugar (até mesmo no disco rígido!). Os endereços que você obtém para a pilha estão em ordem crescente conforme sua árvore de chamadas se torna mais profunda. Os endereços para o heap são imprevisíveis (ou seja, específicos de implementação) e francamente não são importantes. | Para esclarecer, essa resposta contém informações incorretas (thomas corrigiu sua resposta após comentários, legal :)). Outras respostas apenas evitam explicar o que significa alocação estática. Então, explicarei as três formas principais de alocação e como elas geralmente se relacionam ao heap, pilha e segmento de dados abaixo. Também mostrarei alguns exemplos em C / C ++ e Python para ajudar as pessoas a entender. Variáveis "estáticas" (também conhecidas como alocadas estaticamente) não são alocadas na pilha. Não assuma isso - muitas pessoas pensam apenas porque "estático" parece muito com "pilha". Eles realmente não existem na pilha nem no heap. Eles fazem parte do que é chamado de segmento de dados. No entanto, geralmente é melhor considerar "escopo" e "tempo de vida" em vez de "pilha" e "pilha". O escopo refere-se a quais partes do código podem acessar uma variável. Geralmente pensamos em escopo local (só pode ser acessado pela função atual) versus escopo global (pode ser acessado em qualquer lugar), embora o escopo possa ficar muito mais complexo. O tempo de vida se refere a quando uma variável é alocada e desalocada durante a execução do programa. Normalmente pensamos em alocação estática (variávelpersistirá durante toda a duração do programa, tornando-o útil para armazenar as mesmas informações em várias chamadas de função) versus alocação automática (a variável só persiste durante uma única chamada para uma função, tornando-a útil para armazenar informações que são usadas apenas durante e pode ser descartado quando terminar) versus alocação dinâmica (variáveis cuja duração é definida em tempo de execução, em vez de tempo de compilação como estático ou automático). Embora a maioria dos compiladores e interpretadores implementem esse comportamento de maneira semelhante em termos de uso de pilhas, heaps, etc., um compilador pode às vezes quebrar essas convenções se quiser, desde que o comportamento seja correto. Por exemplo, devido à otimização, uma variável local pode existir apenas em um registro ou ser totalmente removida, embora a maioria das variáveis locais existam na pilha. Como foi apontado em alguns comentários, você está livre para implementar um compilador que nem mesmo usa uma pilha ou heap, mas sim alguns outros mecanismos de armazenamento (raramente feitos, já que pilhas e pilhas são ótimas para isso). Fornecerei alguns códigos C anotados simples para ilustrar tudo isso. A melhor maneira de aprender é executar um programa em um depurador e observar o comportamento. Se você preferir ler python, pule para o final da resposta :) // Alocado estaticamente no segmento de dados quando o programa / DLL é carregado pela primeira vez // Desalocado quando o programa / DLL sai // escopo - pode ser acessado de qualquer lugar no código int someGlobalVariable; // Alocado estaticamente no segmento de dados quando o programa é carregado pela primeira vez // Desalocado quando o programa / DLL sai // escopo - pode ser acessado de qualquer lugar neste arquivo de código específico static int someStaticVariable; // "algum Argumento" é alocado na pilha cada vez que MyFunction é chamado // "algumArgumento" é desalocado quando MyFunction retorna // escopo - pode ser acessado apenas em MyFunction () void MyFunction (int someArgument) { // Alocado estaticamente no segmento de dados quando o programa é carregado pela primeira vez // Desalocado quando o programa / DLL sai // escopo - pode ser acessado apenas em MyFunction () static int someLocalStaticVariable; // Alocado na pilha cada vez que MyFunction é chamado // Desalocado quando MyFunction retorna // escopo - pode ser acessado apenas em MyFunction () int someLocalVariable; // Um * ponteiro * é alocado na pilha cada vez que MyFunction é chamado // Este ponteiro é desalocado quando MyFunction retorna // escopo - o ponteiro pode ser acessado apenas em MyFunction () int * someDynamicVariable; // Esta linha faz com que o espaço para um inteiro seja alocado na pilha // quando esta linha é executada. Observe que isso não está no início de // a chamada para MyFunction (), como as variáveis automáticas // escopo - apenas código dentro de MyFunction () pode acessar este espaço // * por meio desta variável particular *. // No entanto, se você passar o endereço em outro lugar, esse código // pode acessá-lo também algumaDynamicVariable = new int; // Esta linha desaloca o espaço para o inteiro no heap. // Se não escrevêssemos, a memória seria "vazada". // Observe uma diferença fundamental entre a pilha e o heap // o heap deve ser gerenciado. A pilha é gerenciada para nós. delete someDynamicVariable; // Em outros casos, em vez de desalocar este espaço de heap, você // pode armazenar o endereço em algum lugar mais permanente para usar mais tarde. // Alguns idiomas até cuidam da desalocação para você ... mas // sempre precisa ser cuidado em tempo de execução por algum mecanismo. // Quando a função retorna, someArgument, someLocalVariable // e o ponteiro someDynamicVariable são desalocados. // O espaço apontado por algumaDynamicVariable já era // desalocado antes de retornar. Retorna; } // Observe que someGlobalVariable, someStaticVariable e // someLocalStaticVariable continua a existir, e não é // desalocado até que o programa saia. Um exemplo particularmente pungente de por que é importante distinguir entre tempo de vida e escopo é que uma variável pode ter escopo local, mas tempo de vida estático - por exemplo, "someLocalStaticVariable" no exemplo de código acima. Essas variáveis podem tornar nossos hábitos de nomenclatura comuns, mas informais, muito confusos. Por exemplo, quando dizemos "local", geralmente queremos dizer "variável alocada automaticamente com escopo local" e quando dizemos global, geralmente queremos dizer "variável alocada estaticamente com escopo global". Infelizmente, quando se trata de coisas como "variáveis alocadas estaticamente com escopo de arquivo", muitas pessoas simplesmente dizem ... "hein ???". Algumas das opções de sintaxe em C / C ++ exacerbam esse problema - por exemplo, muitas pessoas pensam que as variáveis globais não são "estáticas" por causa da sintaxe mostrada abaixo. int var1; // Tem escopo global e alocação estática static int var2; // Tem escopo de arquivo e alocação estática int main () {return 0;} Observe que colocar a palavra-chave "estático" na declaração acima evita que var2 tenha um escopo global. No entanto, o var1 global tem alocação estática. Isso não éintuitivo! Por esse motivo, tento nunca usar a palavra "estático" ao descrever o escopo e, em vez disso, digo algo como escopo "arquivo" ou "arquivo limitado". No entanto, muitas pessoas usam a frase "estático" ou "escopo estático" para descrever uma variável que só pode ser acessada de um arquivo de código. No contexto do tempo de vida, "estático" sempre significa que a variável é alocada no início do programa e desalocada quando o programa é encerrado. Algumas pessoas pensam nesses conceitos como específicos de C / C ++. Eles não são. Por exemplo, o exemplo de Python abaixo ilustra todos os três tipos de alocação (existem algumas diferenças sutis possíveis em linguagens interpretadas que não vou entrar aqui). de datetime import datetime classe Animal: _FavoriteFood = 'Undefined' # _FavoriteFood está estaticamente alocado def PetAnimal (self): curTime = datetime.time (datetime.now ()) # curTime é alocado automaticamente print ("Obrigado por me acariciar. Mas é" + str (curTime) + ", você deve me alimentar. Minha comida favorita é" + self._FavoriteFood) classe Cat (Animal): _FavoriteFood = 'tuna' # Observação, uma vez que substituímos, a classe Cat tem sua própria variável _FavoriteFood alocada estaticamente, diferente da de Animal classe Cachorro (Animal): _FavoriteFood = 'steak' # Da mesma forma, a classe Dog obtém sua própria variável estática. Importante notar - esta variável estática é compartilhada entre todas as instâncias de Dog, portanto, não é dinâmica! if __name__ == "__main__": bigodes = Cat () # Alocado dinamicamente fido = Cachorro () # Alocado dinamicamente rinTinTin = Cachorro () # Alocado dinamicamente bigodes.PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () Dog._FavoriteFood = 'milkbones' bigodes.PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () # A saída é: # Obrigado por me acariciar. Mas é 13: 05: 02.255000, você deveria me alimentar. Minha comida favorita é atum # Obrigado por me acariciar. Mas é 13: 05: 02.255000, você deveria me alimentar. Minha comida favorita é bife # Obrigado por me acariciar. Mas é 13: 05: 02.255000, você deveria me alimentar. Minha comida favorita é bife # Obrigado por me acariciar. Mas é 13: 05: 02.255000, você deveria me alimentar. Minha comida favorita é atum # Obrigado por me acariciar. Mas é 13: 05: 02.255000, você deveria me alimentar. Minha comida favorita são os ossos de leite # Obrigado por me acariciar. Mas é 13: 05: 02.256000, você deveria me alimentar. Minha comida favorita é leite | Outros responderam muito bem aos golpes gerais, então irei acrescentar alguns detalhes. Pilha e pilha não precisam ser singulares. Uma situação comum em que você tem mais de uma pilha é se você tem mais de um encadeamento em um processo. Nesse caso, cada thread tem sua própria pilha. Você também pode ter mais de um heap, por exemplo, algumas configurações de DLL podem resultar na alocação de diferentes DLLs de diferentes heaps, motivo pelo qual geralmente é uma má ideia liberar memória alocada por uma biblioteca diferente. Em C, você pode obter o benefício da alocação de comprimento variável por meio do uso de alloca, que aloca na pilha, em oposição a alocar, que aloca no heap. Essa memória não sobreviverá à sua instrução de retorno, mas é útil para um buffer de rascunho. Criar um enorme buffer temporário no Windows do qual você não usa muito não é de graça. Isso ocorre porque o compilador irá gerar um loop de investigação de pilha que é chamado sempre que sua função é inserida para garantir que a pilha exista (porque o Windows usa uma única página de proteção no final da pilha para detectar quando é necessário aumentar a pilha. Se você acessar a memória em mais de uma página do final da pilha, irá travar). Exemplo: void myfunction () { char big [10000000]; // Faça algo que só use no primeiro 1K ou grande 99% do tempo. } | Outros responderam diretamente à sua pergunta, mas ao tentar entender a pilha e o heap, acho útil considerar o layout de memória de um processo UNIX tradicional (sem threads e alocadores baseados em mmap ()). A página da web Glossário de gerenciamento de memória tem um diagrama desse layout de memória. A pilha e o heap estão tradicionalmente localizados em extremidades opostas do espaço de endereço virtual do processo. A pilha cresce automaticamente quando acessada, até um tamanho definido pelo kernel (que pode ser ajustado com setrlimit (RLIMIT_STACK, ...)). O heap aumenta quando o alocador de memória invoca a chamada do sistema brk () ou sbrk (), mapeando mais páginas de memória física no espaço de endereço virtual do processo. Em sistemas sem memória virtual, como alguns sistemas incorporados, o mesmo layout básico geralmente se aplica, exceto que a pilha e o heap têm tamanho fixo. No entanto, em outros sistemas incorporados (como aqueles baseados em microcontroladores Microchip PIC), a pilha de programa é um bloco separado de memória que não é endereçável por instruções de movimentação de dados e só pode ser modificado ou lido indiretamente por meio de instruções de fluxo de programa (chamada, retorno, etc.). Outras arquiteturas, como processadores Intel Itanium, têm várias pilhas. Nesse sentido, a pilha é um elemento da arquitetura da CPU. | A pilha é uma partede memória que pode ser manipulada por meio de várias instruções em linguagem assembly, como 'pop' (remover e retornar um valor da pilha) e 'push' (colocar um valor na pilha), mas também chamar (chamar uma sub-rotina - isso empurra o endereço para retornar à pilha) e retornar (retorno de uma sub-rotina - isso tira o endereço da pilha e pula para ele). É a região da memória abaixo do registro do ponteiro da pilha, que pode ser configurada conforme necessário. A pilha também é usada para passar argumentos para sub-rotinas e também para preservar os valores nos registros antes de chamar as sub-rotinas. O heap é uma parte da memória fornecida a um aplicativo pelo sistema operacional, normalmente por meio de uma syscall como malloc. Em sistemas operacionais modernos, essa memória é um conjunto de páginas às quais apenas o processo de chamada tem acesso. O tamanho da pilha é determinado em tempo de execução e geralmente não aumenta depois que o programa é iniciado. Em um programa C, a pilha precisa ser grande o suficiente para conter todas as variáveis declaradas em cada função. O heap crescerá dinamicamente conforme necessário, mas o sistema operacional está fazendo a chamada (muitas vezes aumentará o heap em mais do que o valor solicitado por malloc, de modo que pelo menos alguns mallocs futuros não precisem voltar ao kernel para obter mais memória. Esse comportamento costuma ser personalizável) Como você alocou a pilha antes de iniciar o programa, você nunca precisa fazer o malloc antes de usar a pilha, portanto, essa é uma pequena vantagem. Na prática, é muito difícil prever o que será rápido e o que será lento em sistemas operacionais modernos que possuem subsistemas de memória virtual, porque como as páginas são implementadas e onde estão armazenadas é um detalhe de implementação. | O que é uma pilha? Uma pilha é uma pilha de objetos, normalmente uma pilha bem organizada. Pilhas em arquiteturas de computação são regiões da memória onde os dados são adicionados ou removidos da maneira last-in-first-out. Em um aplicativo multi-threaded, cada thread terá sua própria pilha. O que é uma pilha? Uma pilha é uma coleção desordenada de coisas empilhadas ao acaso. Em arquiteturas de computação, o heap é uma área de memória alocada dinamicamente que é gerenciada automaticamente pelo sistema operacional ou pela biblioteca do gerenciador de memória. A memória no heap é alocada, desalocada e redimensionada regularmente durante a execução do programa e isso pode levar a um problema denominado fragmentação. A fragmentação ocorre quando os objetos de memória são alocados com pequenos espaços entre eles, que são muito pequenos para conter objetos de memória adicionais. O resultado líquido é uma porcentagem do espaço de heap que não pode ser usado para outras alocações de memória. Ambos juntos Em um aplicativo multi-threaded, cada thread terá sua própria pilha. Porém, todos os threads diferentes compartilharão o heap. Como os diferentes threads compartilham o heap em um aplicativo multi-threaded, isso também significa que deve haver alguma coordenação entre os threads para que eles não tentem acessar e manipular a (s) mesma (s) peça (s) de memória no heap em o mesmo tempo. O que é mais rápido - a pilha ou a pilha? E porque? A pilha é muito mais rápida do que a pilha. Isso ocorre devido à maneira como a memória é alocada na pilha. Alocar memória na pilha é tão simples quanto mover o ponteiro da pilha para cima. Para pessoas novas em programação, provavelmente é uma boa ideia usar a pilha, pois é mais fácil. Como a pilha é pequena, você deve usá-la quando sabe exatamente de quanta memória vai precisar para seus dados, ou se sabe que o tamanho dos dados é muito pequeno. É melhor usar o heap quando você sabe que precisará de muita memória para seus dados ou apenas não tiver certeza de quanta memória precisará (como com uma matriz dinâmica). Modelo de Memória Java A pilha é a área da memória onde as variáveis locais (incluindo parâmetros do método) são armazenadas. Quando se trata de variáveis de objeto, elas são meramente referências (ponteiros) para os objetos reais no heap. Cada vez que um objeto é instanciado, um pedaço da memória heap é separado para conter os dados (estado) desse objeto. Como os objetos podem conter outros objetos, alguns desses dados podem, de fato, conter referências a esses objetos aninhados. | Acho que muitas outras pessoas deram a você respostas corretas sobre este assunto. Um detalhe que foi esquecido, entretanto, é que a "pilha" provavelmente deveria ser chamada de "loja gratuita". A razão para essa distinção é que o armazenamento gratuito original foi implementado com uma estrutura de dados conhecida como "heap binomial". Por esse motivo, a alocação de implementações anteriores de malloc () / free () era alocação de um heap. No entanto, nos dias atuais, a maioria dos armazenamentos gratuitos é implementada com estruturas de dados muito elaboradas que não são pilhas binomiais. | Você pode fazer algumas coisas interessantes com a pilha. Por exemplo, você tem funções como alloca (assumindo que você pode passar por muitos avisos sobre seu uso), que é uma forma de malloc queusa especificamente a pilha, não o heap, para a memória. Dito isso, os erros de memória baseada em pilha são alguns dos piores que já experimentei. Se você usar a memória heap e ultrapassar os limites do bloco alocado, terá uma boa chance de acionar uma falha de segmento. (Não 100%: seu bloco pode ser acidentalmente contíguo com outro que você alocou anteriormente.) Mas, como as variáveis criadas na pilha são sempre contíguas umas às outras, escrever fora dos limites pode alterar o valor de outra variável. Aprendi que sempre que sinto que meu programa parou de obedecer às leis da lógica, é provável que haja estouro de buffer. | Simplesmente, a pilha é onde as variáveis locais são criadas. Além disso, toda vez que você chama uma sub-rotina, o contador do programa (ponteiro para a próxima instrução da máquina) e quaisquer registradores importantes, e às vezes os parâmetros são colocados na pilha. Então, todas as variáveis locais dentro da sub-rotina são colocadas na pilha (e usadas a partir daí). Quando a sub-rotina termina, todas essas coisas são retiradas da pilha. O PC e os dados de registro são colocados de volta onde estavam quando são removidos, para que o seu programa siga em frente. O heap é a área de memória da qual as alocações de memória dinâmica são feitas (chamadas "novas" ou "alocar" explícitas). É uma estrutura de dados especial que pode rastrear blocos de memória de tamanhos variados e seu status de alocação. Em sistemas "clássicos", a RAM era disposta de forma que o ponteiro da pilha começasse na parte inferior da memória, o ponteiro da pilha começasse no topo e eles crescessem um em direção ao outro. Se eles se sobreporem, você está sem RAM. Porém, isso não funciona com sistemas operacionais multi-threaded modernos. Cada thread deve ter sua própria pilha, e essas podem ser criadas dinamicamente. | De WikiAnwser. Pilha Quando uma função ou método chama outra função que por sua vez chama outra função, etc., a execução de todas essas funções permanece suspensa até que a última função retorne seu valor. Essa cadeia de chamadas de função suspensas é a pilha, porque os elementos na pilha (chamadas de função) dependem uns dos outros. É importante considerar a pilha no tratamento de exceções e nas execuções de threads. Heap O heap é simplesmente a memória usada por programas para armazenar variáveis. O elemento do heap (variáveis) não tem dependências entre si e pode sempre ser acessado aleatoriamente a qualquer momento. | Pilha Acesso muito rápido Não precisa desalocar explicitamente as variáveis O espaço é gerenciado de forma eficiente pela CPU, a memória não se tornará fragmentada Variáveis locais apenas Limite no tamanho da pilha (dependente do sistema operacional) Variáveis não podem ser redimensionadas Heap Variáveis podem ser acessadas globalmente Sem limite de tamanho de memória Acesso (relativamente) mais lento Sem garantia de uso eficiente do espaço, a memória pode ficar fragmentada com o tempo conforme os blocos de memória são alocados e depois liberados Você deve gerenciar a memória (você é responsável por alocar e liberar variáveis) Variáveis podem ser redimensionadas usando realloc () | Em resumo Uma pilha é usada para alocação de memória estática e um heap para alocação de memória dinâmica, ambas armazenadas na RAM do computador. Em detalhe A pilha A pilha é uma estrutura de dados "LIFO" (último a entrar, primeiro a sair), que é gerenciada e otimizada pela CPU de perto. Cada vez que uma função declara uma nova variável, ela é "colocada" na pilha. Então, sempre que uma função é encerrada, todas as variáveis colocadas na pilha por essa função são liberadas (ou seja, são excluídas). Depois que uma variável da pilha é liberada, essa região da memória torna-se disponível para outras variáveis da pilha. A vantagem de usar a pilha para armazenar variáveis é que a memória é gerenciada para você. Você não precisa alocar memória manualmente ou liberá-la quando não precisar mais dela. Além do mais, como a CPU organiza a pilha de memória de forma tão eficiente, ler e gravar nas variáveis da pilha é muito rápido. Mais pode ser encontrado aqui. A pilha O heap é uma região da memória do computador que não é gerenciada automaticamente para você e não é gerenciada com tanta rigidez pela CPU. É uma região de memória mais flutuante (e é maior). Para alocar memória no heap, você deve usar malloc () ou calloc (), que são funções C integradas. Depois de alocar memória no heap, você é responsável por usar free () para desalocar essa memória, uma vez que você não precisa mais dela. Se você não fizer isso, seu programa terá o que é conhecido como vazamento de memória. Ou seja, a memória no heap ainda será reservada (e não estará disponível para outros processos). Como veremos na seção de depuração, existe uma ferramenta chamada Valgrind que pode ajudá-lo a detectar vazamentos de memória. Ao contrário da pilha, o heap não tem restrições de tamanho no tamanho variável (além das limitações físicas óbvias de seu computador). A memória heap é um pouco mais lenta para ser lida e gravada, porque é necessário usar ponteiros para acessar a memória no heap. Falaremos sobre dicas em breve. Ao contrário da pilha,variáveis criadas no heap são acessíveis por qualquer função, em qualquer lugar em seu programa. Variáveis de heap são essencialmente globais em escopo. Mais pode ser encontrado aqui. Variáveis alocadas na pilha são armazenadas diretamente na memória e o acesso a esta memória é muito rápido, e sua alocação é tratada quando o programa é compilado. Quando uma função ou método chama outra função que por sua vez chama outra função, etc., a execução de todas essas funções permanece suspensa até que a última função retorne seu valor. A pilha é sempre reservada em uma ordem LIFO, o bloco reservado mais recentemente é sempre o próximo bloco a ser liberado. Isso torna realmente simples manter o controle da pilha, liberar um bloco da pilha nada mais é do que ajustar um ponteiro. As variáveis alocadas no heap têm sua memória alocada em tempo de execução e o acesso a essa memória é um pouco mais lento, mas o tamanho do heap é limitado apenas pelo tamanho da memória virtual. Os elementos do heap não têm dependências entre si e podem sempre ser acessados aleatoriamente a qualquer momento. Você pode alocar um bloco a qualquer momento e liberá-lo a qualquer momento. Isso torna muito mais complexo controlar quais partes do heap estão alocadas ou livres a qualquer momento. Você pode usar a pilha se souber exatamente quantos dados precisa alocar antes do tempo de compilação, e se não for muito grande. Você pode usar o heap se não souber exatamente de quantos dados precisará no tempo de execução ou se precisar alocar muitos dados. Em uma situação multithread, cada thread terá sua própria pilha completamente independente, mas eles compartilharão o heap. A pilha é específica do segmento e o heap é específico do aplicativo. É importante considerar a pilha no tratamento de exceções e nas execuções de threads. Cada thread obtém uma pilha, embora normalmente haja apenas um heap para o aplicativo (embora não seja incomum ter vários heaps para diferentes tipos de alocação). Em tempo de execução, se o aplicativo precisar de mais heap, ele pode alocar memória da memória livre e, se a pilha precisar de memória, pode alocar memória da memória livre alocada para o aplicativo. Ainda, mais detalhes são fornecidos aqui e aqui. Agora venha para as respostas da sua pergunta. Até que ponto eles são controlados pelo sistema operacional ou pelo tempo de execução da linguagem? O SO aloca a pilha para cada encadeamento no nível do sistema quando o encadeamento é criado. Normalmente, o sistema operacional é chamado pelo tempo de execução da linguagem para alocar o heap para o aplicativo. Mais pode ser encontrado aqui. Qual é o seu escopo? Já fornecido no topo. "Você pode usar a pilha se souber exatamente a quantidade de dados que precisa alocar antes do tempo de compilação e se não for muito grande. Você pode usar a pilha se não souber exatamente de quantos dados precisará no tempo de execução ou se você precisa alocar muitos dados. " Mais pode ser encontrado aqui. O que determina o tamanho de cada um deles? O tamanho da pilha é definido pelo SO quando um thread é criado. O tamanho do heap é definido na inicialização do aplicativo, mas pode aumentar conforme o espaço é necessário (o alocador solicita mais memória do sistema operacional). O que o torna mais rápido? A alocação da pilha é muito mais rápida, pois tudo o que realmente faz é mover o ponteiro da pilha. Usando pools de memória, você pode obter desempenho comparável fora da alocação de heap, mas isso vem com uma pequena complexidade adicional e seus próprios problemas. Além disso, pilha vs. heap não é apenas uma consideração de desempenho; também informa muito sobre o tempo de vida esperado dos objetos. Os detalhes podem ser encontrados aqui. | OK, simplesmente e em palavras curtas, eles significam ordenados e não ordenados ...! Stack: Na pilha de itens, as coisas ficam em cima umas das outras, significa que serão mais rápidos e eficientes para serem processados! ... Então sempre tem um índice para apontar o item específico, o processamento também vai ser mais rápido, tem relação entre os itens também! ... Heap: Sem ordem, o processamento vai ser mais lento e os valores são bagunçados, sem ordem ou índice específico ... são aleatórios e não há relação entre eles ... então o tempo de execução e uso pode variar ... Também crio a imagem abaixo para mostrar como eles podem ser: | pilha, heap e dados de cada processo na memória virtual: | Na década de 1980, o UNIX se propagou como coelhos com grandes empresas lançando suas próprias. A Exxon tinha uma, assim como dezenas de marcas perdidas na história. Como a memória foi disposta ficou a critério de muitos implementadores. Um programa C típico foi projetado na memória com uma oportunidade de aumentar alterando o valor de brk (). Normalmente, o HEAP estava logo abaixo deste valor de brk e aumentar o brk aumentou a quantidade de heap disponível. A única PILHA era normalmente uma área abaixo do HEAP, que era um trato da memória não contendo nada de valor até o início do próximo bloco fixo de memória. Este próximo bloco era frequentemente CODE, que poderia ser substituído pelos dados da pilha em um dos famosos hacks de sua época. Um bloco de memória típico era BSS (um bloco de zerovalores) que acidentalmente não foi zerado na oferta de um fabricante. Outro era DATA contendo valores inicializados, incluindo strings e números. Um terceiro era CODE contendo CRT (tempo de execução C), principal, funções e bibliotecas. O advento da memória virtual no UNIX muda muitas das restrições. Não há nenhuma razão objetiva para que esses blocos precisem ser contíguos, ou de tamanho fixo, ou pedido de uma maneira particular agora. Claro, antes do UNIX havia o Multics, que não sofria com essas restrições. Aqui está um esquema que mostra um dos layouts de memória daquela época. | Alguns centavos: Eu acho que será bom desenhar a memória gráfica e mais simples: Setas - mostram onde aumentam a pilha e o heap, o tamanho da pilha do processo tem limite, definido no sistema operacional, os limites de tamanho da pilha de encadeamento por parâmetros na API de criação de encadeamento normalmente. A pilha geralmente limita pelo tamanho máximo da memória virtual do processo, para 32 bits de 2 a 4 GB, por exemplo. Maneira tão simples: heap de processo é geral para processo e todos os threads dentro, usando para alocação de memória no caso comum com algo como malloc (). Stack é uma memória rápida para armazenamento em variáveis e ponteiros de retorno de função de caso comum, processados como parâmetros na chamada de função, variáveis de função locais. | Uma vez que algumas respostas foram minuciosas, vou contribuir com minha contribuição. Surpreendentemente, ninguém mencionou que várias pilhas de chamadas (ou seja, não relacionadas ao número de threads em execução no nível do sistema operacional) podem ser encontradas não apenas em linguagens exóticas (PostScript) ou plataformas (Intel Itanium), mas também em fibras, threads verdes e algumas implementações de corrotinas. Fibras, fios verdes e co-rotinas são em muitos aspectos semelhantes, o que leva a muita confusão. A diferença entre as fibras e os fios verdes é que as primeiras usam multitarefa cooperativa, enquanto as últimas podem ser cooperativas ou preemptivas (ou mesmo ambas). Para a distinção entre fibras e co-rotinas, veja aqui. Em qualquer caso, o objetivo de ambas as fibras, fios verdes e corrotinas é ter várias funções em execução ao mesmo tempo, mas não em paralelo (veja esta questão do SO para a distinção) em um único segmento de nível de sistema operacional, transferindo o controle de um para o outro de forma organizada. Ao usar fibras, fios verdes ou corrotinas, você geralmente tem uma pilha separada por função. (Tecnicamente, não apenas uma pilha, mas todo um contexto de execução é por função. Mais importante, registros de CPU.) Para cada thread, há tantas pilhas quanto funções em execução simultânea, e o thread está alternando entre a execução de cada função de acordo com a lógica do seu programa. Quando uma função chega ao fim, sua pilha é destruída. Portanto, o número e a vida útil das pilhas são dinâmicos e não são determinados pelo número de threads no nível do sistema operacional! Observe que eu disse "geralmente tem uma pilha separada por função". Existem implementações empilháveis e sem empilhamento de couroutines. As implementações stackful C ++ mais notáveis são Boost.Coroutine e async / await da Microsoft PPL. (No entanto, as funções recuperáveis do C ++ (também conhecidas como "async and await"), que foram propostas para o C ++ 17, provavelmente usam corrotinas sem pilha.) A proposta de fibras para a biblioteca padrão C ++ está em breve. Além disso, existem algumas bibliotecas de terceiros. Threads verdes são extremamente populares em linguagens como Python e Ruby. | Tenho algo a compartilhar, embora os pontos principais já tenham sido abordados. Pilha Acesso muito rápido. Armazenado na RAM. As chamadas de função são carregadas aqui junto com as variáveis locais e parâmetros de função passados. O espaço é liberado automaticamente quando o programa sai de um escopo. Armazenado na memória sequencial. Heap Acesso lento comparativamente ao Stack. Armazenado na RAM. As variáveis criadas dinamicamente são armazenadas aqui, o que mais tarde requer a liberação da memória alocada após o uso. Armazenado onde quer que seja feita a alocação de memória, acessado sempre por um ponteiro. Nota interessante: Se as chamadas de função tivessem sido armazenadas em heap, isso resultaria em 2 pontos confusos: Devido ao armazenamento sequencial na pilha, a execução é mais rápida. O armazenamento em heap teria resultado em um grande consumo de tempo, tornando a execução do programa inteiro mais lenta. Se as funções fossem armazenadas em heap (armazenamento bagunçado apontado por ponteiro), não haveria como retornar ao endereço do chamador de volta (que pilha fornece devido ao armazenamento sequencial na memória). | Uau! Tantas respostas e acho que nenhuma delas acertou ... 1) Onde e o que estão (fisicamente na memória de um computador real)? A pilha é a memória que começa como o endereço de memória mais alto alocado para a imagem do programa e, a partir daí, seu valor diminui. É reservado para parâmetros de função chamados e para todas as variáveis temporárias usadas em funções. Existem dois montes: público e privado. O heap privado começa em um limite de 16 bytes (para programas de 64 bits) ou um limite de 8 bytes (para programas de 32 bits) após o último byte de código em seu programa e, em seguida, aumenta emvalor a partir daí. Também é chamado de heap padrão. Se o heap privado ficar muito grande, ele se sobreporá à área da pilha, assim como a pilha se sobreporá ao heap se ficar muito grande. Como a pilha começa em um endereço mais alto e segue seu caminho até o endereço mais baixo, com o hack adequado você pode fazer a pilha tão grande que vai invadir a área de heap privada e sobrepor a área de código. O truque, então, é sobrepor o suficiente da área de código para que você possa conectar no código. É um pouco complicado de fazer e você corre o risco de travar o programa, mas é fácil e muito eficaz. O heap público reside em seu próprio espaço de memória fora do espaço de imagem do programa. É essa memória que será desviado para o disco rígido se os recursos de memória ficarem escassos. 2) Até que ponto eles são controlados pelo sistema operacional ou pelo tempo de execução da linguagem? A pilha é controlada pelo programador, o heap privado é gerenciado pelo sistema operacional e o heap público não é controlado por ninguém porque é um serviço do sistema operacional - você faz solicitações e elas são concedidas ou negadas. 2b) Qual é o seu escopo? Todos são globais para o programa, mas seu conteúdo pode ser privado, público ou global. 2c) O que determina o tamanho de cada um deles? O tamanho da pilha e o heap privado são determinados pelas opções de tempo de execução do compilador. O heap público é inicializado no tempo de execução usando um parâmetro de tamanho. 2d) O que o torna mais rápido? Eles não foram projetados para serem rápidos, mas úteis. A forma como o programador os utiliza determina se são "rápidos" ou "lentos" REF: https://norasandler.com/2019/02/18/Write-a-Compiler-10.html https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate | Muitas respostas são conceitos corretos, mas devemos notar que uma pilha é necessária para o hardware (ou seja, microprocessador) para permitir a chamada de sub-rotinas (CALL em linguagem assembly ..). (OOP caras vão chamá-lo de métodos) Na pilha, você salva os endereços de retorno e chama → push / ret → pop é gerenciado diretamente no hardware. Você pode usar a pilha para passar parâmetros ... mesmo que seja mais lento do que usando registradores (diria um guru do microprocessador ou um bom livro de BIOS dos anos 1980 ...) Sem pilha, nenhum microprocessador pode funcionar. (não podemos imaginar um programa, mesmo em linguagem assembly, sem sub-rotinas / funções) Sem a pilha, pode. (Um programa em linguagem assembly pode funcionar sem, já que o heap é um conceito de sistema operacional, como malloc, que é uma chamada de OS / Lib. O uso da pilha é mais rápido, pois: É hardware, e até mesmo push / pop são muito eficientes. malloc requer entrar no modo kernel, usar bloqueio / semáforo (ou outros primitivos de sincronização) executando algum código e gerenciar algumas estruturas necessárias para controlar a alocação. | Heap é uma área de memória alocada dinamicamente que é gerenciada automaticamente pelo sistema operacional ou pela biblioteca do gerenciador de memória. Você pode alocar um bloco a qualquer momento e liberá-lo a qualquer momento. A alocação de heap requer a manutenção de um registro completo de qual memória está alocada e não, bem como alguma manutenção de sobrecarga para reduzir a fragmentação, encontrar segmentos de memória contíguos grandes o suficiente para caber no tamanho solicitado e assim por diante A memória pode ser desalocada a qualquer momento, deixando espaço livre. À medida que o heap cresce, novos blocos são freqüentemente alocados de endereços inferiores para endereços superiores. Portanto, você pode pensar no heap como um heap de blocos de memória que aumenta de tamanho conforme a memória é alocada. Se o heap for muito pequeno para uma alocação, o tamanho pode ser aumentado com a aquisição de mais memória do sistema operacional subjacente. A memória alocada do heap permanecerá alocada até que ocorra uma das seguintes situações: A memória é liberada O programa termina Pilha: Armazenado na RAM do computador exatamente como o heap. As variáveis criadas na pilha sairão do escopo e serão desalocadas automaticamente. Muito mais rápido para alocar em comparação com as variáveis na pilha. Armazena dados locais, endereços de retorno, usados para passagem de parâmetros. Pode haver um estouro de pilha quando muito da pilha é usado (principalmente de recursão infinita ou muito profunda, alocações muito grandes). Você usaria a pilha se soubesse exatamente de quantos dados precisa para alocar antes do tempo de compilação e não é muito grande. Normalmente tem um tamanho máximo já determinado quando seu programa começa. Pilha: Armazenado na RAM do computador exatamente como a pilha. Em C ++, as variáveis no heap devem ser destruídas manualmente e nunca cair fora do escopo. Os dados são liberados com delete, delete [] ou free. Mais lento para alocar em comparação com as variáveis na pilha. Usado sob demanda para alocar um bloco de dados para uso pelo programa. Pode haver fragmentação quando há muitas alocações e desalocações. Em C ++ ou C, os dados criados no heap serão apontados por ponteiros e alocado com novo ou malloc respectivamente. Pode haver falhas de alocação se um buffer muito grande for solicitado para ser alocado. Vocêusaria o heap se você não souber exatamente quantos dados você precisará em tempo de execução ou se você precisar alocar muitos dados. Responsável por vazamentos de memória. | A pilha é essencialmente uma memória de fácil acesso que simplesmente gerencia seus itens como uma pilha bem. Somente itens cujo tamanho é conhecido com antecedência podem ir para a pilha. Esse é o caso de números, strings, booleanos. O heap é uma memória para itens dos quais você não pode predeterminar o tamanho e estrutura exatos. Uma vez que objetos e matrizes podem sofrer mutação e mudar em tempo de execução, eles têm que ir para o heap. Fonte: Academind | A pilha e o heap da CPU estão fisicamente relacionados a como a CPU e os registros funcionam com a memória, como a linguagem de montagem de máquina funciona, não as próprias linguagens de alto nível, mesmo que essas linguagens possam decidir pequenas coisas. Todas as CPUs modernas funcionam com a "mesma" teoria do microprocessador: todas são baseadas no que é chamado de "registradores" e algumas são para "empilhar" para obter desempenho. Todas as CPUs possuem stack registers desde o início e sempre estiveram por aqui, jeito de falar, pelo que eu sei. As linguagens assembly são as mesmas desde o início, apesar das variações ... até a Microsoft e sua Linguagem Intermediária (IL) que mudou o paradigma para ter uma linguagem assembly de máquina virtual OO. Portanto, poderemos ter alguma CPU CLI / CIL no futuro (um projeto da MS). As CPUs têm registros de pilha para acelerar o acesso às memórias, mas são limitados em comparação ao uso de outros registros para obter acesso total a toda a memória disponível para o processus. É por isso que falamos sobre alocações de pilha e heap. Em resumo, e em geral, o heap é grande e lento e é para instâncias "globais" e conteúdo de objetos, já que a pilha é pequena e rápida e para variáveis e referências "locais" (ponteiros ocultos para esquecer de gerenciá-los). Portanto, quando usamos a nova palavra-chave em um método, a referência (um int) é criada na pilha, mas o objeto e todo o seu conteúdo (tipos de valor, bem como objetos) são criados no heap, se bem me lembro. Mas os tipos de valores elementares locais e os arrays são criados na pilha. A diferença no acesso à memória está no nível de referência das células: endereçar o heap, a memória geral do processo, requer mais complexidade em termos de tratamento de registros de CPU do que a pilha que é "mais" localmente em termos de endereçamento porque a pilha de CPU registrar é usado como endereço base, se bem me lembro. É por isso que quando temos chamadas ou loops de recurse muito longos ou infinitos, obtemos estouro de pilha rapidamente, sem congelar o sistema nos computadores modernos ... C # Heap (ing) Vs Stack (ing) em .NET Stack vs Heap: Conheça a Diferença Alocação de memória de classe estática onde é armazenado C # O que e onde estão a pilha e o heap? https://en.wikipedia.org/wiki/Memory_management https://en.wikipedia.org/wiki/Stack_register Recursos de linguagem assembly: Tutorial de programação de montagem Manuais do desenvolvedor de software das arquiteturas Intel® 64 e IA-32 | Obrigado por uma boa discussão, mas como um verdadeiro novato, eu me pergunto onde as instruções são mantidas? No INÍCIO os cientistas estavam decidindo entre duas arquiteturas (von NEUMANN onde tudo é considerado DATA e HARVARD onde uma área da memória era reservada para instruções e outra para dados). No final das contas, optamos pelo design de Von Neumann e agora tudo é considerado 'igual'. Isso tornou mais difícil para mim quando eu estava aprendendo montagem https://www.cs.virginia.edu/~evans/cs216/guides/x86.html porque eles falam sobre registradores e ponteiros de pilha. Tudo acima fala sobre DATA. Meu palpite é que, uma vez que uma instrução é uma coisa definida com uma pegada de memória específica, ela iria para a pilha e, portanto, todos 'aqueles' registradores discutidos em assembly estão na pilha. É claro que então veio a programação orientada a objetos com instruções e dados agrupados em uma estrutura que era dinâmica, então agora as instruções seriam mantidas no heap também? | Questão altamente ativa. Ganhe 10 reputação para responder a esta pergunta. O requisito de reputação ajuda a proteger essa pergunta contra spam e atividades sem resposta. Não é a resposta que você está procurando? Procure outras questões etiquetadas pilha de gerenciamento de memória - agnóstico de linguagem heap - alocação de memória dinâmica ou faça sua própria pergunta.